﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Threading;
using JasLib.Extensions;
using JasLib.Threading;
using JasLib.Wpf;

namespace WpfFizzBuzz.ViewModels
{
    public class MainViewModel : ViewModelBase<MainViewModel>
    {
        private int _iRangeMinimum = 1;
        public int RangeMinimum
        {
            get { return _iRangeMinimum; }
            set { SetProperty(ref _iRangeMinimum, value, () => RangeMinimum); }
        }

        private int _iRangeMaximum = 100;
        public int RangeMaximum
        {
            get { return _iRangeMaximum; }
            set { SetProperty(ref _iRangeMaximum, value, () => RangeMaximum); }
        }

        private ObservableCollection<FactorViewModel> _factors = null;
        public ObservableCollection<FactorViewModel> Factors
        {
            get
            {
                if (_factors == null)
                    _factors = new ObservableCollection<FactorViewModel>();
                return _factors;
            }
        }

        private FactorInfo[] _factorsInProcess = null;

        private ObservableCollection<MultipleViewModel> _multiples = null;
        public ObservableCollection<MultipleViewModel> Multiples
        {
            get
            {
                if (_multiples == null)
                    _multiples = new ObservableCollection<MultipleViewModel>();
                return _multiples;
            }
        }

        private bool _bIsFindingMultiples = false;
        public bool IsFindingMultiples
        {
            get { return _bIsFindingMultiples; }
            private set { SetProperty(ref _bIsFindingMultiples, value, () => IsFindingMultiples); }
        }

        private EasyBindCommand _startFindMultiplesCommand = null;
        public EasyBindCommand StartFindMultiplesCommand
        {
            get
            {
                if (_startFindMultiplesCommand == null)
                {
                    _startFindMultiplesCommand = new EasyBindCommand(this.DoStartFindMultiples);

                    /// Add more conditions as needed...
                    MultiBinding binding = new MultiBinding();
                    binding.Converter = new NullableBooleanAndConverter();
                    binding.AddPropertyPathBinding(this, vm => vm.IsFindingMultiples, new NullableBooleanNotConverter());
                    _startFindMultiplesCommand.SetBinding(EasyBindCommand.CanExecuteProperty, binding);
                }
                return _startFindMultiplesCommand;
            }
        }

        private async void DoStartFindMultiples(object objParam)
        {
            this.IsFindingMultiples = true;

            try
            {
                this.Multiples.Clear();
                this.Factors.ForEach(f => f.MatchCount = 0);

                /// Make a copy of the viewmodel data, to be accessed from the thread pool.
                _factorsInProcess = this.Factors.Select(vm => new FactorInfo(vm, vm.Name, vm.Factor)).AsArray();

                await ThreadingUtilities.ForAsync(_iRangeMinimum, _iRangeMaximum, CancellationToken.None,
                    async (i, state) =>
                    {
                        await AsyncThreadSwitcher.UnsafeSwitchToThreadPoolAsync();

                        var factors = _factorsInProcess.Where(f => (i % f._iFactor) == 0).Select(f => f._parentViewModel).AsArray();
                        if (factors.Length > 0)
                        {
                            await this.SwitchToDispatcherAsync(DispatcherPriority.Background);

                            var multiple = new MultipleViewModel(i, factors);
                            this.Multiples.BinarySearchInsert(m => m.Multiple, multiple);

                            foreach (var factorViewModel in factors)
                                factorViewModel.MatchCount++;
                        }
                    });
            }
            catch (Exception ex)
            {
            }
            finally
            {
            }

            await this.SwitchToDispatcherAsync();
            this.IsFindingMultiples = false;

            return;
        }
    }
}
